package com.doordu.framework.cloud.xxl.registry;

import com.doordu.framework.cloud.xxl.DoorduJobInstanceConfig;
import com.doordu.framework.cloud.xxl.enums.HandlerTypeEnums;
import com.doordu.framework.cloud.xxl.manager.ExecutorManager;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.executor.XxlJobExecutor;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.xxl.job.core.handler.impl.MethodJobHandler;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.client.serviceregistry.Registration;
import org.springframework.context.ApplicationContext;
import org.springframework.core.MethodIntrospector;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.reflect.Method;
import java.net.URI;
import java.util.Map;
import java.util.Set;

/**
 * 用于处理注册状态
 *
 * @author zxw
 * @date 2020/9/21 14:16
 */
public class DoorduJobRegistration implements Registration {

    private DoorduJobInstanceConfig config;
    private ExecutorManager executorManager;

    private static final Log log = LogFactory.getLog(DoorduJobRegistration.class);


    public static Builder builder(DoorduJobInstanceConfig doorduJobInstanceConfig) {
        return new Builder(doorduJobInstanceConfig);
    }

    public DoorduJobRegistration(DoorduJobInstanceConfig doorduJobInstanceConfig, ExecutorManager executorManager) {
        this.config = doorduJobInstanceConfig;
        this.executorManager = executorManager;
    }

    public DoorduJobInstanceConfig getDoorduJobInstanceConfig() {
        return config;
    }

    public ExecutorManager getExecutorManager() {
        return executorManager;
    }

    public void injectJobMenthod(ApplicationContext context) {
        if (context == null) {
            return;
        }
        // init job handler from method
        String[] beanDefinitionNames = context.getBeanNamesForType(Object.class, false, true);
        for (String beanDefinitionName : beanDefinitionNames) {
            Object bean = context.getBean(beanDefinitionName);

            Map<Method, XxlJob> annotatedMethods = null;
            try {
                annotatedMethods = MethodIntrospector.selectMethods(bean.getClass(),
                        new MethodIntrospector.MetadataLookup<XxlJob>() {
                            @Override
                            public XxlJob inspect(Method method) {
                                return AnnotatedElementUtils.findMergedAnnotation(method, XxlJob.class);
                            }
                        });
            } catch (Throwable ex) {
                log.error("xxl-job method-jobhandler resolve error for bean[" + beanDefinitionName + "].", ex);
            }
            if (annotatedMethods == null || annotatedMethods.isEmpty()) {
                continue;
            }

            for (Map.Entry<Method, XxlJob> methodXxlJobEntry : annotatedMethods.entrySet()) {
                Method method = methodXxlJobEntry.getKey();
                XxlJob xxlJob = methodXxlJobEntry.getValue();
                if (xxlJob == null) {
                    continue;
                }

                String name = xxlJob.value();
                if (name.trim().length() == 0) {
                    throw new RuntimeException("xxl-job method-jobhandler name invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
                }
                if (XxlJobExecutor.loadJobHandler(name) != null) {
                    throw new RuntimeException("xxl-job jobhandler[" + name + "] naming conflicts.");
                }

                // execute method
                if (!(method.getParameterTypes().length == 1 && method.getParameterTypes()[0].isAssignableFrom(String.class))) {
                    throw new RuntimeException("xxl-job method-jobhandler param-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " +
                            "The correct method format like \" public ReturnT<String> execute(String param) \" .");
                }
                if (!method.getReturnType().isAssignableFrom(ReturnT.class)) {
                    throw new RuntimeException("xxl-job method-jobhandler return-classtype invalid, for[" + bean.getClass() + "#" + method.getName() + "] , " +
                            "The correct method format like \" public ReturnT<String> execute(String param) \" .");
                }
                method.setAccessible(true);

                // init and destory
                Method initMethod = null;
                Method destroyMethod = null;

                if (xxlJob.init().trim().length() > 0) {
                    try {
                        initMethod = bean.getClass().getDeclaredMethod(xxlJob.init());
                        initMethod.setAccessible(true);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException("xxl-job method-jobhandler initMethod invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
                    }
                }
                if (xxlJob.destroy().trim().length() > 0) {
                    try {
                        destroyMethod = bean.getClass().getDeclaredMethod(xxlJob.destroy());
                        destroyMethod.setAccessible(true);
                    } catch (NoSuchMethodException e) {
                        throw new RuntimeException("xxl-job method-jobhandler destroyMethod invalid, for[" + bean.getClass() + "#" + method.getName() + "] .");
                    }
                }
                Class jobHandler = HandlerTypeEnums.choiceJobHandler(config.getHandlerName());
//                jobHandler.getDeclaredConstructor();
                executorManager.registerExecutor(name, new MethodJobHandler(bean, method, initMethod, destroyMethod));
            }
        }
    }

    @Override

    public String getServiceId() {
        return config.getAppName();
    }

    @Override
    public String getHost() {
        return config.getHost();
    }

    @Override
    public int getPort() {
        return config.getPort();
    }

    @Override
    public boolean isSecure() {
        return false;
    }

    @Override
    public URI getUri() {
        return null;
    }

    @Override
    public Map<String, String> getMetadata() {
        return null;
    }

    public static class Builder {
        private DoorduJobInstanceConfig doorduJobInstanceConfig;
        private ExecutorManager executorManager;

        public Builder(DoorduJobInstanceConfig doorduJobInstanceConfig) {
            this.doorduJobInstanceConfig = doorduJobInstanceConfig;
        }

        public Builder with(ExecutorManager executorManager) {
            this.executorManager = executorManager;
            return this;
        }

        public DoorduJobRegistration build() {
            return new DoorduJobRegistration(doorduJobInstanceConfig, executorManager);
        }
    }
}
